/**
 * 
 */
package any.desktop.server.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.Validate;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Field.Index;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.Field.TermVector;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.store.Directory;
import org.apache.tika.config.TikaConfig;
import org.apache.tika.exception.TikaException;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.parser.Parser;
import org.apache.tika.sax.WriteOutContentHandler;
import org.apache.tika.utils.ParseUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;

import any.desktop.FileItem;
import any.desktop.server.DesktopIndexer;
import any.desktop.server.IndexException;

/**
 * @author keke <keke@codehaus.org>
 * @version
 * @revision $Revision:$
 */
public class DefaultDesktopIndexer implements DesktopIndexer {
	private static class TempFileReader extends FileReader {
		private boolean		deleted	= false;
		private final File	tempFile;

		public TempFileReader(final File file) throws FileNotFoundException {
			super(file);
			tempFile = file;
		}

		@Override
		public void close() throws IOException {
			super.close();
			deleteTempFile();
		}

		private void deleteTempFile() {
			if (!deleted) {
				deleted = tempFile.delete();
			}
		}

		@Override
		protected void finalize() throws Throwable {
			super.finalize();
			deleteTempFile();
		}

	}

	private static final Logger	LOG	= LoggerFactory
	                                        .getLogger(DefaultDesktopIndexer.class);
	private Analyzer	        analyzer;
	private Directory	        directory;
	private IndexWriter	        indexWriter;

	private TikaConfig	        tikaConfig;

	public void destory() throws CorruptIndexException, IOException {
		if (LOG.isInfoEnabled()) {
			LOG.info("To close IndexWriter");

		}
		flushAndCommit();
	}

	public void flushAndCommit() throws IOException {
		if (LOG.isInfoEnabled()) {
			LOG.info("Flush and commint changes");
		}
		if (indexWriter != null) {
			indexWriter.flush();
			indexWriter.optimize();
			indexWriter.close();
			indexWriter = null;
		}
	}

	public DesktopIndexer index(final FileItem item) throws IOException,
	        IndexException {
		Validate.notNull(item);
		if (LOG.isDebugEnabled()) {
			LOG.debug("To index file Item: {}", item);
		}
		try {
			final Metadata metadata = new Metadata();
			final File temp = File.createTempFile("index", "-temp");
			temp.deleteOnExit();
			final FileWriter out = new FileWriter(temp);
			final WriteOutContentHandler handler = new WriteOutContentHandler(
			        out);
			final File file = new File(item.getItemURI());
			final Parser parser = ParseUtils.getParser(file, tikaConfig);
			if (parser != null) {
				parser.parse(new FileInputStream(new File(item.getItemURI())),
				        handler, metadata);
				if (LOG.isDebugEnabled()) {
					LOG.debug("{} was parsed", item);
					LOG.debug("Metadata are: {}",metadata);
				}
				final Document doc = new Document();
				doc.add(new Field("id", item.getMd5(), Store.YES,
				        Index.UN_TOKENIZED));

				doc.add(new Field("type", FilenameUtils.getExtension(
				        file.getName()).toLowerCase(), Store.YES,
				        Index.UN_TOKENIZED));
				doc.add(new Field("title", FilenameUtils.getBaseName(file
				        .getName()), Store.YES, Index.TOKENIZED));
				doc.add(new Field("path", item.getItemURI().toString(),
				        Store.YES, Index.NO));
				doc.add(new Field("content", new TempFileReader(temp),
				        TermVector.WITH_POSITIONS_OFFSETS));
				getIndexWriter().updateDocument(new Term("id", item.getMd5()),
				        doc);
			} else {
				if (LOG.isWarnEnabled()) {
					LOG.warn("Unable parse {}, no parser for type was found",
					        item);
				}
			}
		} catch (final TikaException e) {
			throw new IndexException(e);
		} catch (final SAXException e) {
			throw new RuntimeException(e);
		}
		return this;
	}

	public void init() throws IOException, TikaException {
		Validate.notNull(directory);

		tikaConfig = TikaConfig.getDefaultConfig();
	}

	public void setAnalyzer(final Analyzer analyzer) {
		this.analyzer = analyzer;
	}

	public void setDirectory(final Directory directory) {
		this.directory = directory;
	}

	private IndexWriter getIndexWriter() throws IOException {
		if (indexWriter == null) {
			final boolean create = !IndexReader.indexExists(directory);
			if (IndexReader.isLocked(directory)) {
				IndexReader.unlock(directory);
			}
			indexWriter = new IndexWriter(directory, false, analyzer, create);
		}
		return indexWriter;
	}
}
